/* 
 * Copyright 2011 VelocitiSoftware
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.velociti.ikarus.widget.client.ui;

import java.util.Iterator;

import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasAnimation;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
/**
 * ######VIkarusSlidingPanel##### 
 * -Look at slidingpanel.css for styling.
 * -Needs JQuery js.
 * @author Alper Turkyilmaz - VelocitiSoftware - 2011
 * @version 1.0.2
 * 
 */
public final class VIkarusSlidingPanel extends Composite implements HasAnimation, HasOpenHandlers<VIkarusSlidingPanel>, HasCloseHandlers<VIkarusSlidingPanel>, Paintable {

	private static final String CLASSNAME = "v-ikarusslidingpanel";
	private static final String CLASSNAME_HEADER = "header";
	private static final String CLASSNAME_CONTENT = "content";
	public static final int BEGINING = 10;
	public static final int MIDDLE = 20;
	public static final int END = 30;

	protected String id;
	protected ApplicationConnection client;

	// Orientations
	private static final int EDGE_LEFT = 0;
	private static final int EDGE_RIGHT = 1;
	private static final int EDGE_TOP = 2;
	private static final int EDGE_BOTTOM = 3;

	protected static int Z_INDEX = 20000;

	int HEADER_SIZE_SHORTDIM = 15;
	int HEADER_SIZE_LONGDIM = 100;
	int HIDDEN_SIZE = 0;
	int size = 0;
	int edge = EDGE_LEFT;
	private int headerLocation = MIDDLE;
	private int location = MIDDLE;

	private FlowPanel mainPanel = new FlowPanel();

	private final SimplePanel contentWrapper = new SimplePanel();
	private final Header header = new Header();
	private boolean isAnimationEnabled = true;
	private int duration = 1000;
	private boolean isOpen = false;

	private final class Header extends SimplePanel {
		private Header() {
			super(DOM.createAnchor());
			Element elem = getElement();
			DOM.setElementProperty(elem, "href", "javascript:void(0);");
			sinkEvents(Event.ONCLICK);
		}

		public void onBrowserEvent(Event event) {
			switch (DOM.eventGetType(event)) {
			case Event.ONCLICK:
				DOM.eventPreventDefault(event);
				toggle();
			}
		}
	}

	public VIkarusSlidingPanel() {
		initWidget(mainPanel);
		mainPanel.setStyleName(CLASSNAME);
		setZIndex(Z_INDEX);
		Window.addResizeHandler(new ResizeHandler() {
			public void onResize(ResizeEvent event) {
				setLocation();
				layoutAndStyleHeader();

				if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
					contentWrapper.setHeight(mainPanel.getElement().getClientHeight() + "px");
				} else {
					contentWrapper.setWidth(mainPanel.getElement().getClientWidth() + "px");
				}

			}
		});
	}

	public int getEdge() {
		return edge;
	}

	public HandlerRegistration addCloseHandler(CloseHandler<VIkarusSlidingPanel> handler) {
		return addHandler(handler, CloseEvent.getType());
	}

	public HandlerRegistration addOpenHandler(OpenHandler<VIkarusSlidingPanel> handler) {
		return addHandler(handler, OpenEvent.getType());
	}

	public void clear() {
		setContent(null);
	}

	public Widget getContent() {
		return contentWrapper.getWidget();
	}

	public Widget getHeader() {
		return header.getWidget();
	}

	public boolean isAnimationEnabled() {
		return isAnimationEnabled;
	}

	public boolean isOpen() {
		return isOpen;
	}

	public void setAnimationEnabled(boolean enable) {
		isAnimationEnabled = enable;
	}

	public void setContent(Widget content) {
		final Widget currentContent = getContent();
		if (currentContent != null) {
			contentWrapper.setWidget(null);
		}
		if (content != null) {
			contentWrapper.setWidget(content);
			switch (edge) {
			case EDGE_RIGHT:
			case EDGE_LEFT:
				content.setHeight(contentWrapper.getElement().getClientHeight() + "px");
				client.updateVariable(id, "contentheight", contentWrapper.getElement().getClientHeight() + "px", false);
				client.updateVariable(id, "contentwidth", size + "px", false);

				break;
			case EDGE_TOP:
			case EDGE_BOTTOM:
				content.setWidth(contentWrapper.getElement().getClientWidth() + "px");
				client.updateVariable(id, "contentwidth", contentWrapper.getElement().getClientWidth() + "px", false);
				client.updateVariable(id, "contentheight", size + "px", false);
				break;
			}
			client.sendPendingVariableChanges();
		}
	}

	public void setHeader(Widget headerWidget) {
		header.setWidget(headerWidget);

	}

	int getOffset() {
		if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
			return HEADER_SIZE_SHORTDIM + (contentWrapper.getElement().getOffsetWidth() - contentWrapper.getElement().getClientWidth());

		} else if (edge == EDGE_TOP) {
			return HEADER_SIZE_SHORTDIM + (contentWrapper.getElement().getOffsetHeight() - contentWrapper.getElement().getClientHeight());

		} else {
			return HEADER_SIZE_SHORTDIM + (contentWrapper.getElement().getOffsetHeight() - contentWrapper.getElement().getClientHeight());

		}

	}

	public void toggle() {
		String toSize = isOpen ? "0" : String.valueOf(size);

		switch (edge) {
		case EDGE_LEFT:
		case EDGE_RIGHT:
			toggleHorizontal(duration, isAnimationEnabled(), toSize, id, getOffset());
			break;
		case EDGE_TOP:
		case EDGE_BOTTOM:
			toggleVertical(duration, isAnimationEnabled(), toSize, id, getOffset());
			break;
		}
		isOpen = !isOpen;
	}

	void setHeaderLocation() {
		if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
			int top = 0;
			switch (headerLocation) {
			case BEGINING:
				top = 0;
				break;
			case MIDDLE:
				top = (mainPanel.getElement().getOffsetHeight() - header.getElement().getOffsetHeight()) / 2;
				break;
			case END:
				top = (mainPanel.getElement().getOffsetHeight() - header.getElement().getOffsetHeight());
				break;
			}
			header.getElement().getStyle().setTop(top, Unit.PX);
		}

		if (edge == EDGE_TOP || edge == EDGE_BOTTOM) {
			int left = 0;
			switch (headerLocation) {
			case BEGINING:
				left = 0;
				break;
			case MIDDLE:
				left = (mainPanel.getElement().getOffsetWidth() - header.getElement().getOffsetWidth()) / 2;
				break;
			case END:
				left = (mainPanel.getElement().getOffsetWidth() - header.getElement().getOffsetWidth());
				break;
			}
			header.getElement().getStyle().setLeft(left, Unit.PX);
		}
	}

	void layout() {
		mainPanel.getElement().setId("mainpanel" + id);
		if (edge == EDGE_BOTTOM) {
			mainPanel.add(header);
			layoutAndStyleHeader();
			mainPanel.add(contentWrapper);
			layoutAndStyleContentWrapper();

		} else {
			mainPanel.add(contentWrapper);
			layoutAndStyleContentWrapper();
			mainPanel.add(header);
			layoutAndStyleHeader();
		}

	}

	void layoutAndStyleHeader() {
		header.getElement().setId("header" + id);
		header.setStylePrimaryName(CLASSNAME);
		header.getElement().getStyle().setPosition(Position.ABSOLUTE);
		if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
			header.setWidth(HEADER_SIZE_SHORTDIM + "px");
			header.setHeight(HEADER_SIZE_LONGDIM + "px");
			if (edge == EDGE_LEFT) {
				header.setStyleDependentName("header-left", true);
				DOM.setStyleAttribute(header.getElement(), "cssFloat", "left");
			} else {
				header.setStyleDependentName("header-right", true);
				DOM.setStyleAttribute(header.getElement(), "cssFloat", "right");
			}

		} else {
			header.setHeight(HEADER_SIZE_SHORTDIM + "px");
			header.setWidth(HEADER_SIZE_LONGDIM + "px");
			if (edge == EDGE_TOP) {
				header.setStyleDependentName("header-top", true);
				header.getElement().getStyle().setDisplay(Display.BLOCK);

			} else {
				header.setStyleDependentName("header-bottom", true);
				header.getElement().getStyle().setDisplay(Display.BLOCK);

			}
		}
		setHeaderLocation();
	}

	void layoutAndStyleContentWrapper() {
		contentWrapper.getElement().setId("contentpanel" + id);
		contentWrapper.getElement().getStyle().setPadding(0, Unit.PX);
		contentWrapper.getElement().getStyle().setOverflow(Overflow.HIDDEN);
		contentWrapper.setStylePrimaryName(CLASSNAME);
		contentWrapper.setStyleDependentName("content", true);

		if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
			if (edge == EDGE_LEFT) {
				contentWrapper.getElement().getStyle().setProperty("borderLeftWidth", 0, Unit.PX);
				DOM.setStyleAttribute(contentWrapper.getElement(), "cssFloat", "left");
			} else {
				contentWrapper.getElement().getStyle().setProperty("borderRightWidth", 0, Unit.PX);
				DOM.setStyleAttribute(contentWrapper.getElement(), "cssFloat", "right");
			}
			size = mainPanel.getElement().getClientWidth() - getOffset();
			contentWrapper.setHeight(mainPanel.getElement().getClientHeight() + "px");
			contentWrapper.setWidth((isOpen() ? size : HIDDEN_SIZE) + "px");
			mainPanel.setWidth((isOpen() ? size : HEADER_SIZE_SHORTDIM) + "px");
		} else {
			if (edge == EDGE_TOP) {
				contentWrapper.getElement().getStyle().setProperty("borderTopWidth", 0, Unit.PX);
			} else {
				contentWrapper.getElement().getStyle().setProperty("borderBottomWidth", 0, Unit.PX);
				contentWrapper.getElement().getStyle().setPosition(Position.RELATIVE);
				contentWrapper.getElement().getStyle().setTop(15, Unit.PX);
			}
			size = mainPanel.getElement().getClientHeight() - getOffset();
			contentWrapper.setWidth(mainPanel.getElement().getClientWidth() + "px");
			contentWrapper.setHeight((isOpen() ? size : HIDDEN_SIZE) + "px");
			mainPanel.setHeight((isOpen() ? size : HEADER_SIZE_SHORTDIM) + "px");
		}
	}

	public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
		this.client = client;
		id = uidl.getId();
		if (uidl.hasAttribute("edge")) {
			edge = uidl.getIntAttribute("edge");
		}
		if (uidl.hasAttribute("headerlocation")) {
			headerLocation = uidl.getIntAttribute("headerlocation");
		}
		if (uidl.hasAttribute("location")) {
			location = uidl.getIntAttribute("location");
		}
		if (uidl.hasAttribute("animenabled")) {
			isAnimationEnabled = uidl.getBooleanAttribute("animenabled");
		}
		if (uidl.hasAttribute("open")) {
			isOpen = uidl.getBooleanAttribute("open");
		}
		if (uidl.hasAttribute("duration")) {
			duration = uidl.getIntAttribute("duration");
		}
		if (client.updateComponent(this, uidl, false)) {
			return;
		}
		Widget content = null;
		for (final Iterator<Object> i = uidl.getChildIterator(); i.hasNext();) {
			final UIDL r = (UIDL) i.next();
			final Paintable child = client.getPaintable(r);
			child.updateFromUIDL(r, client);
			content = (Widget) child;
		}
		setLocation();
		layout();
		setContent(content);
		attachPanel();
	}

	void attachPanel() {
		if (RootPanel.get().getWidgetIndex(this) == -1) {
			RootPanel.get().add(this);
			RootPanel.detachOnWindowClose(this);
		}
	}

	void setLocation() {
		Element elem = getElement();
		elem.getStyle().setPosition(Position.ABSOLUTE);

		if (edge == EDGE_LEFT || edge == EDGE_RIGHT) {
			if (location == BEGINING) {
				elem.getStyle().setPropertyPx("top", 0);
			} else if (location == MIDDLE) {
				int top = (client.getView().getOffsetHeight() - getOffsetHeight()) / 2;
				elem.getStyle().setPropertyPx("top", top);
			} else {
				elem.getStyle().setPropertyPx("bottom", 0);
			}
		} else {
			if (location == BEGINING) {
				elem.getStyle().setPropertyPx("left", 0);
			} else if (location == MIDDLE) {
				int left = (client.getView().getOffsetWidth() - getOffsetWidth()) / 2;
				elem.getStyle().setPropertyPx("left", left);
			} else {
				elem.getStyle().setPropertyPx("right", 0);
			}
		}
		String sEdge = "";
		int size = 0;
		switch (edge) {
		case EDGE_LEFT:
			sEdge = "left";
			break;
		case EDGE_RIGHT:
			sEdge = "right";
			break;
		case EDGE_BOTTOM:
			sEdge = "bottom";
			size = elem.getOwnerDocument().getBody().getOffsetHeight() - Window.getClientHeight();
			VConsole.log("size : " + size);
			break;
		case EDGE_TOP:
			sEdge = "top";
			break;
		}
		elem.getStyle().setPropertyPx(sEdge, size);

	}

	protected void setZIndex(int zIndex) {
		DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex);
	}

	public static native void toggleHorizontal(int duration, boolean animate, String toSize, String id, int offset)
	/*-{
	 		var duration = duration;
	 		if(!animate){
	 			duration = 0;
	 		}
	  		$wnd.$("#contentpanel"+id).animate(
				{width: toSize},
				{duration: duration,step: function( now, fx ){
	  				$wnd.$("#mainpanel"+id).css( "width", now+offset );
				}
			});
	}-*/;

	public static native void toggleVertical(int duration, boolean animate, String toSize, String id, int offset)
	/*-{
			var duration = duration;
	 		if(!animate){
	 			duration = 0;
	 		}
			$wnd.$("#contentpanel"+id).animate(
				{height: toSize},
				{duration: duration,
				step: function( now, fx ){
	  				$wnd.$("#mainpanel"+id).css( "height", now+offset );
	  				
				}
			});
	}-*/;
}
